#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

// psthname和proj_id随机
std::string g_pathname = ".";
int g_proj_id = 0x66;

int g_def_size = 4096; // 大小要求是4kb的整数倍

// 共享内存
class SharedMemory
{
private:
    bool GreateHelper(int flags)
    {
        // 生成键值
        _key = ftok(g_pathname.c_str(), g_proj_id);
        if (_key < 0)
        {
            perror("ftok");
            return false;
        }
        printf("键值为: 0x%x\n", _key);

        // 创建shm
        _shmid = shmget(_key, _size, flags); // 设置权限,实现挂载到虚拟地址空间
        if (_shmid < 0)
        {
            perror("shmget");
            return false;
        }
        printf("shmid为: %d\n", _shmid);

        return true;
    }

public:
    SharedMemory(int size = g_def_size)
        : _size(size), _shmid(-1), _key(0),
          _start_addr(nullptr), _datastart(nullptr), _num(nullptr),
          _windex(0), _rindex(0)
    {
    }

    // 创建
    bool Create()
    {
        return GreateHelper(IPC_CREAT | IPC_EXCL | 0666);
    }
    bool Get()
    {
        return GreateHelper(IPC_CREAT);
    }

    // 共享内存挂载到虚拟地址空间
    bool Attach()
    {
        _start_addr = shmat(_shmid, nullptr, 0);
        if ((long long)_start_addr == -1)
        {
            perror("shmat");
            return false;
        }
        //划分地址
        _num = (int *)_start_addr;
        _datastart = (char *)_start_addr + sizeof(int);

        std::cout << "挂载成功" << std::endl;
        printf("_start_addr: %p\n", _start_addr);

        return true;
    }
    void SetZero()
    {
        *_num = 0;
    }

    //写操作
    void Push(char ch)
    {
        if(*_num == _size)
            return;
        ((char *)_datastart)[_windex++] = ch;
        ((char *)_datastart)[_windex] = '\0';
        printf("Push: %c\n", ch);
        _windex %= _size; // 防止越界
        (*_num)++;
    }

    //读操作
    void Pop(char *ch)
    {
        if(*_num == 0)
            return;
        *ch = ((char *)_datastart)[_rindex++];
        _rindex %= _size;
        (*_num)--;
        // printf("%s\n", _datastart);

    }

    //共享内存属性打印
    void Printshmid_ds()
    {
        struct shmid_ds ds;
        int n = shmctl(_shmid, IPC_STAT, &ds);
        if(n < 0)
        {
            perror("shmctl");
            return;
        }

        //输出属性
        printf("key: 0x%x\n", ds.shm_perm.__key);
        printf("atime: %lu\n", ds.shm_atime);
        printf("size: %ld\n", ds.shm_segsz);
        printf("nattch: %ld\n", ds.shm_nattch);

    }

    // 去挂载
    bool Detach()
    {
        int n = shmdt(_start_addr);
        if (n < 0)
        {
            perror("shmdt");
            return false;
        }
        std::cout << "去挂载成功" << std::endl;
        return true;
    }

    bool Remove()
    {
        // 删除shm
        int n = shmctl(_shmid, IPC_RMID, nullptr);
        if (n < 0)
        {
            perror("shmctl");
            return false;
        }
        std::cout << "shm删除成功" << std::endl;
        return true;
    }

    ~SharedMemory() {}

private:
    key_t _key;
    int _size;
    int _shmid;

    void *_start_addr; // 共享内存虚拟地址
    char *_datastart;  // 数据内容开始地址
    int *_num;         // 表明shm中存放数据个数的起始位置

    int _windex;       // 写：访问虚拟地址的下标
    int _rindex;       // 写：访问虚拟地址的下标
};
